﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity;
using System.Data.Entity.Core;
using System.Data.Entity.Core.EntityClient;
using System.Data.Entity.Core.Objects.DataClasses;
using System.Data.SqlClient;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using System.Transactions;

namespace GrayParrot.Data.Obsolete
{
    [Obsolete]
    public abstract class FacadeBase<Context> 
        where Context : DbContext, new()
    {
        private Exception m_lastExceptionOccurred = null;
        //private Repository.RepositoryBase<Context> m_repository = null;
        private static object _lock = new object();

        private IDictionary<string, IRepository> m_repos = null;

        public IDictionary<string, IRepository> Repositories
        {
            get { return m_repos; }
            set { m_repos = value; }
        }

        //public Repository.RepositoryBase<Context> Repository
        //{
        //    get { return m_repository; }
        //    //set { m_repository = value; }
        //}

        public Exception LastExceptionOccurred
        {
            get { return m_lastExceptionOccurred; }
            set { m_lastExceptionOccurred = value; }
        }

        private static ConcurrentDictionary<Type, DbContext> m_contexts = new ConcurrentDictionary<Type, DbContext>();
        //int m_pageSize;

        public FacadeBase()
        {
            Initialize();

            //m_repository = new Repository.RepositoryBase<Context>(new Context());
        }

        public FacadeBase(params IRepository[] repositories)
        {
            if (m_repos == null)
            {
                m_repos = new Dictionary<string, IRepository>();

                foreach (IRepository r in repositories)
                {
                    m_repos.Add("ControlsRepository2", r);
                }
            }

            Initialize();

            //m_repository = new Repository.RepositoryBase<Context>(new Context());
        }

        //public static Context GetContext()
        //{
        //    Context m_c = null;

        //    if (m_contexts.ContainsKey(typeof(Context)))
        //    {
        //        m_c = (Context)m_contexts[typeof(Context)];
        //    }
        //    else
        //    {
        //        m_c = new Context();

        //        //m_contexts.Add(typeof(Context), m_c);
        //        m_contexts.AddOrUpdate(typeof(Context), m_c, (k, v) => v);
        //    }

        //    if (m_c.Connection.State == System.Data.ConnectionState.Closed) m_c.Connection.Open();

        //    return m_c;
        //}

        protected abstract void Initialize();
        protected virtual void OnTransactionSuccess() { }
        public virtual void OnTransactionFailure(Exception failure, [CallerMemberName] string memberName = "", [CallerFilePath] string sourceFilePath = "", [CallerLineNumber] int sourceLineNumber = 0) { }
        public virtual void OnFailure(Exception failure, [CallerMemberName] string memberName = "", [CallerFilePath] string sourceFilePath = "", [CallerLineNumber] int sourceLineNumber = 0) { }

        public IRepository GetRepository(string key)
        {
            return m_repos[key];
        }

        public Context GetContext()
        {
            Context m_c = null;

            try
            {
                lock (_lock)
                {
                    if (m_contexts.ContainsKey(typeof(Context)))
                    {
                        m_c = (Context)m_contexts[typeof(Context)];
                    }
                    else
                    {
                        m_c = new Context();
                        
                        //m_contexts.Add(typeof(Context), m_c);
                        m_contexts.AddOrUpdate(typeof(Context), m_c, (k, v) => v);
                    }

                    if (m_c.Database.Connection.State == System.Data.ConnectionState.Closed) m_c.Database.Connection.Open();
                }
            }
            catch (Exception ex)
            {
                m_lastExceptionOccurred = ex;
                OnFailure(ex);
            }

            return m_c;
        }

        public Context GetContext(string connection)
        {
            Context m_c = null;

            try
            {
                lock (_lock)
                {
                    if (m_contexts.ContainsKey(typeof(Context)))
                    {
                        m_c = (Context)m_contexts[typeof(Context)];
                    }
                    else
                    {
                        m_c = (Context)Activator.CreateInstance(typeof(Context), connection);

                        //m_contexts.Add(typeof(Context), m_c);
                        m_contexts.AddOrUpdate(typeof(Context), m_c, (k, v) => v);
                    }

                    if (m_c.Database.Connection.State == System.Data.ConnectionState.Closed) m_c.Database.Connection.Open();
                }
            }
            catch (Exception ex)
            {
                m_lastExceptionOccurred = ex;
                OnFailure(ex);
            }


            return m_c;
        }

        public void RunAsync<T>(Func<T> action, BackgroundWorkerCallbackBase.Callback callback)
        {
            try
            {
                lock (_lock)
                {
                    BackGroundWorkerWrapper<T> p = new BackGroundWorkerWrapper<T>(action, callback);
                    p.RunWorkerAsync();
                }
            }
            catch (Exception ex)
            {
                m_lastExceptionOccurred = ex;
                OnFailure(ex);
            }
        }

        public void RunAsync<T>(Func<T> action, BackgroundWorkerCallbackBase.Callback callback, params object[] argument)
        {
            try
            {
                lock (_lock)
                {
                    BackGroundWorkerWrapper<T> p = new BackGroundWorkerWrapper<T>(action, callback);
                    p.RunWorkerAsync(argument);
                }
            }
            catch (Exception ex)
            {
                m_lastExceptionOccurred = ex;
                OnFailure(ex);
            }
        }

        public void UseTryCatch(Action action, Action finallyAction = null)
        {
            try
            {
                action();
            }
            catch (Exception ex)
            {
                m_lastExceptionOccurred = ex;
                OnFailure(ex);
            }
            finally
            {
                if (finallyAction != null)
                {
                    finallyAction();
                }
            }
        }

        public void Transact(Action action)
        {
            try
            {
                using (TransactionScope transaction = new TransactionScope())
                {
                    action();
                    transaction.Complete();

                    OnTransactionSuccess();
                }
            }
            catch (Exception ex)
            {
                m_lastExceptionOccurred = ex;
                OnFailure(ex);
            }
        }

        public void Transact(Action action, DbContext context)
        {
            try
            {
                using (TransactionScope transaction = new TransactionScope())
                {
                    action();
                    context.SaveChanges();
                    transaction.Complete();
                }
            }
            catch (Exception ex)
            {
                m_lastExceptionOccurred = ex;
                OnFailure(ex);
            }
        }

        public void Transact(Action action, TransactionScopeOption scopeOption)
        {
            try
            {
                using (TransactionScope transaction = new TransactionScope(scopeOption))
                {
                    action();
                    transaction.Complete();
                }
            }
            catch (Exception ex)
            {
                m_lastExceptionOccurred = ex;
                OnFailure(ex);
            }
        }

        public void Transact(Action action, DbContext context, TransactionScopeOption scopeOption)
        {
            try
            {
                using (TransactionScope transaction = new TransactionScope(scopeOption))
                {
                    action();
                    context.SaveChanges();
                    transaction.Complete();
                }
            }
            catch (Exception ex)
            {
                m_lastExceptionOccurred = ex;
                OnFailure(ex);
            }
        }

        public void ManageConcurrency(Action action, params bool[] returnValue)
        {
            try
            {
                action();
            }
            catch (OptimisticConcurrencyException ocexp)
            {
                EntityObject oo = (EntityObject)ocexp.StateEntries[0].Entity;
                
                string msg = string.Format("- {0} - ATTENZIONE! I dati che stai per salvare sono stati modificati da un altro utente.", "ciccio");

                //System.Windows.Forms.MessageBox.Show(msg);
                //{
                //    // Resolve the concurrency conflict by refreshing the 
                //    // object context before re-saving changes. 
                //    //GetContext().Refresh(RefreshMode.StoreWins, ocexp.StateEntries[0].Entity);

                //    // Save changes.
                //    //GetContext().SaveChanges();

                //    //Console.WriteLine("OptimisticConcurrencyException " + "handled and changes saved");
                //}

                returnValue.SetValue(false, 0);

                m_lastExceptionOccurred = ocexp;
                OnFailure(ocexp);
            }
        }

        public string GetADOConnectionString()
        {
            EntityConnection ec = (EntityConnection)this.GetContext().Database.Connection;
            SqlConnection sc = (SqlConnection)ec.StoreConnection;
            string adoConnStr = sc.ConnectionString;
            return adoConnStr;
        }

        public string GetADOConnectionString(string password)
        {
            EntityConnection ec = (EntityConnection)this.GetContext().Database.Connection;
            SqlConnection sc = (SqlConnection)ec.StoreConnection;
            string adoConnStr = string.Format("{0};password={1};", sc.ConnectionString, password);
            return adoConnStr;
        }
    }
}
